﻿//
// Copyright (c) 2024 BPer
//
#include <bperice/BPerRtcSocket.h>
#include <bperice/BPerRtcStun.h>

#include <bperrtc/BPerRtcRtcp.h>
#include <bperrtc/BPerPushH264.h>
#include <bperrtc/BPerPushH265.h>
#include <bperrtc/BPerBandwidth.h>
#include <bperrtc/BPerPlayStream.h>
#include <bperrtc/BPerPushStream.h>
#include <bperrtp/BPerRtpConstant.h>
#include <bperrtp/BPerRtcpCompound.h>
#include <bperrtc/BPerRtcConnection.h>

#include <bperutil/sys/BPerLog.h>
#include <bperutil/BPerAvcType.h>
#include <bperutil/sys/BPerSsrc.h>
#include <bperutil/sys/BPerSRtp.h>

#include <bpersdp/BPerSdp.h>
#include <bpersdp/BPerAnswerSdp.h>


static void g_session_receive(char *data, int32_t nb_data, void *user) {
	if (user == NULL)		return;
	BPerRtcConnection *conn = (BPerRtcConnection*) user;
	conn->receive(conn->session,data,nb_data);

}

void bPer_rtcconn_startStunTimer(BPerRtcSession *session);

static void g_bPer_startStunTimer(void *user) {
	if (user == NULL)		return;
	BPerRtcConnection *conn = (BPerRtcConnection*) user;
	bPer_rtcconn_startStunTimer(conn->session);

}

static void bPer_onConnectionStateChange(BPerRtcSession *session,BPerRtcConnectionState state){
	if(session->context.streamConfig&&session->context.streamConfig->iceCallback.onConnectionStateChange){
						session->context.streamConfig->iceCallback.onConnectionStateChange(
								session->context.streamConfig->iceCallback.context,
								session->context.streamConfig->uid,
								state);
	}
}

static void g_bPer_doTask(int32_t taskId, void *user) {
	if (user == NULL)	return;
	BPerRtcSession *session = (BPerRtcSession*) user;

	if (!session->isControlled && session->isSendStun && taskId == 1) {

		if (session->context.stun.data&& session->context.stun.nb > 0) {

			if(session->context.sock->write(&session->context.sock->session,session->context.stun.data, session->context.stun.nb)!=BPer_Ok){
				bPer_error("send stun fail!");
			}
			if(session->context.state==BPer_Conn_State_New) {
				session->context.state=BPer_Conn_State_Connecting;
				bPer_onConnectionStateChange(session,BPer_Conn_State_Connecting);
			}
		}

	}
	if(session->context.state!=BPer_Conn_State_Connected) return;


	if (session->startRecv&&session->play) {

		if (taskId == 1) {

			if (session->play->send_rtcp_rr(&session->context,session->play->playStream))	bPer_error("RTCP Error:RR err ");
#if BPer_Enable_RtcpXr
			if (session->context.streamConfig->streamDirection==BPerRecvonly && session->play->send_rtcp_xr_rrtr(&session->context,session->play->playStream))
				bPer_error("RTCP Error:XR err ");
#endif

		}

		if (taskId == 100) {
#if BPer_Enable_TWCC
			if (session->play->send_periodic_twcc(&session->context,session->play->playStream))
				bPer_error("RTCP Error:send twcc err ");
#endif

		}

	}

	if(session->push){
			if (taskId == 1) {
				if (session->push->send_rtcp_sr(&session->context,session->push->pubStream))		bPer_error("send rtcp sr Error ");
				if (session->push->check_bandwidth(&session->context,session->push->pubStream))		bPer_error("check bandwidth Error ");
				if (session->push->check_twcc(&session->context,session->push->pubStream))		bPer_error("check twcc Error ");
#if BPer_Enable_RTC_Video
				if(session->play&&session->context.stats.recvStats.video.rtt>0 )
							session->play->update_rtt(&session->context,session->play->playStream,session->play->playStream->videoTrack->session.track.ssrc,session->context.stats.recvStats.video.rtt);
#endif

			}
		}


}



static void bPer_rtcconn_init(BPerRtcSession *session, BPerRtcDirection role) {
	if (session == NULL)	return;

	session->videoCodec =(BPerVideoCodec) session->context.avinfo->video.videoEncoderType;
	session->isSendDtls = 0;

	session->sessionTimeout=session->context.avinfo->rtc.sessionTimeout;
    if (role == BPerSendonly || role==BPerSendrecv)  {
#if BPer_Enable_RTC_Audio
    	if(session->pushAudio==NULL){
    		session->pushAudio=(BPerPushAudio*) bPer_calloc(1,sizeof(BPerPushAudio));
    	}
#endif
#if BPer_Enable_RTC_Video

    	session->pushH264 = NULL;
        if (session->videoCodec == BPer_VED_H264) {
            session->pushH264 = (BPerPushH264*) bPer_calloc(1,sizeof(BPerPushH264));
        }
	#if	BPer_Enable_H265_Encoding
        session->pushH265 = NULL;
        if (session->videoCodec == BPer_VED_H265) {
            session->pushH265 = (BPerPushH265*) bPer_calloc(1,sizeof(BPerPushH265));
        }
	#endif


#endif
    }
	//  session->20ms
	session->tm_1s = (BPerCTimer*) bPer_calloc(1, sizeof(BPerCTimer));
	bPer_create_timer(session->tm_1s, session, 1, 1000);
	session->tm_1s->doTask = g_bPer_doTask;

	session->tm_100ms = (BPerCTimer*) bPer_calloc(1, sizeof(BPerCTimer));
	bPer_create_timer(session->tm_100ms, session, 100, 100);
	session->tm_100ms->doTask = g_bPer_doTask;

	session->startRecv = 0;
	session->isSendStun = BPerFalse;

#if BPer_Enable_Dtls
	bPer_create_rtcdtls(session->context.dtls,session->isControlled);
	session->context.dtls->session.sslCallback=&session->context.streamConfig->sslCallback;
	session->context.dtls->session.uid=session->context.streamConfig->uid;
#endif


	if (role == BPerRecvonly || role==BPerSendrecv) {
		if(session->playRtpBuffer == NULL) {
			session->playRtpBuffer = (BPerRtpBuffer*) bPer_calloc(1,sizeof(BPerRtpBuffer));
			bPer_create_rtpBuffer(session->playRtpBuffer, 1500, kRtpPacketSize);
		}
		if (session->play == NULL) {
			session->play = (BPerRtcPlay*) bPer_calloc(1,sizeof(BPerRtcPlay));
			bPer_create_rtcplay(&session->context, session->play,session->playRtpBuffer);

		}
	}

	if (role == BPerSendonly || role==BPerSendrecv)  {

#if BPer_Enable_RTC_Video
		if(session->pushVideoRtpBuffer == NULL) {
			session->pushVideoRtpBuffer = (BPerRtpBuffer*) bPer_calloc(1,sizeof(BPerRtpBuffer));
			bPer_create_rtpBuffer(session->pushVideoRtpBuffer, 1400, kRtpPacketSize);
		}
#endif
#if BPer_Enable_RTC_Audio
		if(session->pushAudio){
			if(session->pushAudioRtpBuffer == NULL) {
					session->pushAudioRtpBuffer = (BPerRtpBuffer*) bPer_calloc(1,sizeof(BPerRtpBuffer));
					bPer_create_rtpBuffer(session->pushAudioRtpBuffer, 100, kRtpPacketSize);
			}
			bPer_create_pushAudio(session->pushAudio, session->pushAudioRtpBuffer);
		}
#endif

#if BPer_Enable_RTC_Video
		if (session->pushH264) {
			bPer_create_pushH264(session->pushH264, session->pushVideoRtpBuffer);
		}
	#if	BPer_Enable_H265_Encoding
		if (session->pushH265) {
			bPer_create_pushH265(session->pushH265,  session->pushVideoRtpBuffer);
		}
	#endif

#endif
		if (session->push == NULL) {
			session->push = (BPerRtcPush*) bPer_calloc(1,sizeof(BPerRtcPush));
			bPer_create_rtcpush(session->push,session->context.audioSsrc, session->context.videoSsrc);
		}
	}

	session->activeState = BPerTrue;
}

static int32_t bPer_rtcconn_on_rtcp_feedback_twcc(BPerRtcSession *session,BPerRtcpCommon *rtcp) {
#if BPer_Enable_TWCC
	session->context.twcc.decode(&session->context.twcc.session,rtcp);
#endif
	return BPer_Ok;
}

static int32_t bPer_rtcconn_on_rtcp_feedback_remb(BPerRtcSession *session,	BPerRtcpCommon *rtcp) {

	return BPer_Ok;
}


static int32_t bPer_rtcconn_dispatch_rtcp(BPerRtcSession *session,BPerRtcpCommon *rtcp) {
	int32_t err = BPer_Ok;
	uint16_t rtcpType = rtcp->header.type;
	// For TWCC packet.
	if (BPerRtcpType_rtpfb == rtcpType && 15 == rtcp->header.rc) {
		return bPer_rtcconn_on_rtcp_feedback_twcc(session, rtcp);
	}

	// For REMB packet.
	if (BPerRtcpType_psfb == rtcpType) {

		if (15 == rtcp->header.rc) {
			return bPer_rtcconn_on_rtcp_feedback_remb(session, rtcp);
		}
	}

	// Ignore special packet.
	if (BPerRtcpType_rr == rtcpType) {
		if (rtcp->rb->ssrc == 0) {
			return err;
		}
	}

	if (session->push	&& BPer_Ok	!= (err = session->push->on_rtcp(&session->context,session->push->pubStream, rtcp))) {
		return bPer_error_wrap(err, "handle publish rtcp");
	}

	if (session->play	&& BPer_Ok!= (err = session->play->on_rtcp(&session->context,	session->play->playStream, rtcp))) {
		return bPer_error_wrap(err, "handle play rtcp");
	}

	return err;
}

static void bPer_rtcconn_setSsrc(BPerRtcSession *session, uint32_t audioSsrc,
		uint32_t videoSsrc) {
	if(session==NULL) return;
#if BPer_Enable_RTC_Audio
	if(session->pushAudio)
		session->pushAudio->push->audioSsrc=audioSsrc;
#endif
#if BPer_Enable_RTC_Video
	if (session->pushH264)
			session->pushH264->push->videoSsrc = videoSsrc;
	#if	BPer_Enable_H265_Encoding
	if (session->pushH265)
		session->pushH265->push->videoSsrc = videoSsrc;
	#endif

	#if BPer_Enable_AV1_Encoding

	#endif

	#if	BPer_Enable_MJpeg_Encoding


	#endif
#endif



}

 void bPer_rtcconn_startStunTimer(BPerRtcSession *session) {

	if (session->tm_1s&&!session->tm_1s->isStart)
		bPer_timer_start(session->tm_1s);
	session->isSendStun = BPerTrue;
}

static void bPer_rtcconn_startTimers(BPerRtcSession *session) {
	if (session->tm_1s&&!session->tm_1s->isStart)		bPer_timer_start(session->tm_1s);

	// if (session->20ms&&!session->20ms->session->isStart)		session->20ms->start();
#if BPer_Enable_TWCC
	BPerStreamDirection opt=session->context.streamConfig->streamDirection;
	if (session->context.twccId>0&&(opt==BPerRecvonly||opt==BPerSendrecv)&&session->tm_100ms&&!session->tm_100ms->isStart)
		bPer_timer_start(session->tm_100ms);
#endif
}

static int32_t bPer_rtcconn_on_rtcp(BPerRtcSession *session, char *data,int32_t nb_data) {
	int32_t err = BPer_Ok;
	int32_t nb_unprotected_buf = nb_data;
#if BPer_Enable_Dtls
	if ((err = bPer_dec_rtcp(&session->context.srtp, data, &nb_unprotected_buf))!= BPer_Ok) {
		if (err == srtp_err_status_replay_fail)	return BPer_Ok;
		return bPer_error_wrap(err, "rtcp unprotect");
	}
#endif
	char *unprotected_buf = data;
	BPerBuffer buffer;
	bPer_init_buffer(&buffer, unprotected_buf, nb_unprotected_buf);

	if (BPer_Ok != (err = bPer_decode_rtcpCompound(&session->rtcp_compound, &buffer))) {
		return bPer_error_wrap(err, "decode rtcp plaintext=%u",	nb_unprotected_buf);
	}

	BPerRtcpCommon *rtcp = NULL;
	for (int i = 0; i < session->rtcp_compound.rtcpVector.vsize; i++) {
		rtcp = &session->rtcp_compound.rtcpVector.payload[i];
		err = bPer_rtcconn_dispatch_rtcp(session, rtcp);
		if (BPer_Ok != err) {
			bPer_rtcpCompound_clear(&session->rtcp_compound);
			return bPer_error_wrap(err,
					"cipher=%u, plaintext=%u,  rtcp=(%u,%u,%u,%u)", nb_data,
					nb_unprotected_buf, rtcp->nb_data, rtcp->header.rc,
					rtcp->header.type, rtcp->ssrc);
		}
	}
	bPer_rtcpCompound_clear(&session->rtcp_compound);
	return err;

}

static BPerBool bPer_rtcconn_isAlive(BPerRtcSession* session){
	if(session==NULL||session->context.state!=BPer_Conn_State_Connected) return BPerFalse;
	return session->lastStunTime + session->sessionTimeout > bPer_get_system_time();
}


static void bPer_rtcconn_startudp(BPerRtcSession *session) {
	bPer_rtcconn_init(session, session->context.streamConfig->direction);
	bPer_rtcconn_setSsrc(session, session->context.audioSsrc,session->context.videoSsrc);
}


static int32_t bPer_rtcconn_send_video_meta(BPerRtcSession *session, BPerFrame *p) {
#if BPer_Enable_RTC_Video
	if (session->pushH264)
		return session->pushH264->on_spspps(session,session->pushH264->push, p);
	#if	BPer_Enable_H265_Encoding
	if (session->pushH265)
		return session->pushH265->on_spspps(session,session->pushH265->push, p);
	#endif

#endif
	return BPer_Ok;
}

static int32_t bPer_rtcconn_onVideo(BPerRtcSession *session, BPerFrame *p) {
#if BPer_Enable_RTC_Video
	#if BPer_Enable_Dtls
	if (session==NULL || p==NULL || session->context.state!=BPer_Conn_State_Connected||session->context.dtls->session.state!=BPerDtlsStateClientDone)	return BPer_Ok;
	#else
	if (session==NULL|| p==NULL || session->context.state!=BPer_Conn_State_Connected)	return BPer_Ok;
	#endif
	if (p->frametype == BPER_FrameType_Spspps)
		return bPer_rtcconn_send_video_meta(session, p);
	if (session->pushH264)
		return session->pushH264->on_video(session, session->pushH264->push, p);
	#if	BPer_Enable_H265_Encoding
	if (session->pushH265)
		return session->pushH265->on_video(session, session->pushH265->push, p);
	#endif

#endif
	return BPer_Ok;

}

static int32_t bPer_rtcconn_onAudio(BPerRtcSession *session, BPerFrame *p) {

#if BPer_Enable_Dtls
	if (session==NULL||p==NULL||session->context.state!=BPer_Conn_State_Connected||session->context.dtls->session.state!=BPerDtlsStateClientDone)	return BPer_Ok;
#else
	if (session==NULL||p==NULL||session->context.state!=BPer_Conn_State_Connected)	return BPer_Ok;
#endif
#if BPer_Enable_RTC_Audio
	if (session->pushAudio)
		return session->pushAudio->on_audio(session, session->pushAudio->push, p);
#endif

	return BPer_Ok;

}

static int32_t bPer_rtcconn_onMessage(BPerRtcSession *session, BPerFrame *p) {
#if BPer_Enable_Dtls
#if BPer_Enable_Datachannel
	if(session==NULL || p==NULL ||session->context.state!=BPer_Conn_State_Connected||
			session->context.dtls->session.isRecvAlert||session->context.dtls->session.state!=BPerDtlsStateClientDone)
		return BPer_Ok;

	if(session->datachannel&&session->datachannel->send_message) session->datachannel->send_message(session->datachannel->context,p);
#endif
#endif
	return BPer_Ok;
}

static void bPer_rtcconn_close(BPerRtcSession *session) {
	if (session == NULL)	return;
#if BPer_Enable_Dtls
	session->context.dtls->session.isSendAlert = BPerTrue;
	if (session->context.dtls&&!session->context.dtls->session.isRecvAlert&&session->context.dtls->sendDtlsAlert){
		session->context.dtls->sendDtlsAlert(&session->context.dtls->session);
	}



#else
	if(session->isControlled==0){
		char alerts[15];
		bPer_memset(alerts,0,15);
		alerts[0]=30;
		for(int i=0;i<5;i++){
			bPer_usleep(1000*20);
			if (bPer_rtc_sendData(session->context.sock, alerts, 15)	!= BPer_Ok) {
				bPer_error("send error");
			}
		}
	}
#endif
	session->context.state = BPer_Conn_State_Closed;
	bPer_onConnectionStateChange(session,BPer_Conn_State_Closed);
}


static int32_t bPer_rtcconn_notify(BPerRtcSession *session, BPerRtcMessageType mess) {
#if BPer_Enable_RTC_Video
	if(session==NULL||session->play==NULL||session->play->playStream->videoTrack==NULL) return ERROR_RTC_CALLBACK;
	BPerPlayTrackVideo *videotrack=session->play->playStream->videoTrack;
	if (mess == BPerRTC_Decoder_Error) {
		if(videotrack) videotrack->setRequestKeyframeState(&videotrack->session,BPerFalse);
		return bPer_send_rtcp_fb_pli(&session->context, videotrack->session.track.ssrc);
	}
#endif
	return BPer_Ok;
}

static void bPer_rtcconn_receive(BPerRtcSession *session, char *data, int32_t size) {
	if (session==NULL||!session->activeState)	return;

	uint8_t bt=(uint8_t)data[0];
	session->lastStunTime=bPer_get_system_time();
	//is rtp rtcp
	if(bt > 127 && bt < 192 && size>12){

		bt=(uint8_t)data[1];
		if (bt>= 192 && bt <= 223) {//rtcp
			bPer_rtcconn_on_rtcp(session, data, size);
			return;
		}
		//rtp
		session->startRecv = 1;
		if (session->play)	session->play->on_rtp(&session->context, session->play->playStream,data, size);
		return;
	}
	//is stun
	if(size>0&&(bt==0x00 || bt==0x01)){
		int32_t err = 0;
		if(bt==0x00){
			BPerStunPacket request;
			bPer_memset(&request,0,sizeof(BPerStunPacket));

			if ((err = session->ice.session.stun.decode(&request,data, size)) != BPer_Ok) {
				bPer_error("decode stun packet failed");
				session->context.state=BPer_Conn_State_Failed;
				bPer_onConnectionStateChange(session,BPer_Conn_State_Failed);
				return;
			}
			if ((err =session->ice.session.stun.createResponseStunPacket(&request,session)) != BPer_Ok) {
				bPer_error("create response stun packet failed");
				return;
			}
			if(session->context.state==BPer_Conn_State_New) {
				session->context.state=BPer_Conn_State_Connecting;
				bPer_onConnectionStateChange(session,BPer_Conn_State_Connecting);
			}

		}else if(bt==0x01&&data[1]==0x01){
			if ((err = session->ice.session.stun.decode2(data, size)) != BPer_Ok) {
				bPer_error("decode stun packet failed");
				return;
			}

#if BPer_Enable_Dtls
			if (!session->isSendDtls) {
				if (session->context.dtls->startHandShake(&session->context.dtls->session)) bPer_error("dtls start handshake failed!");
				session->isSendDtls = BPerTrue;
			}
#else
			if(session->context.state==BPer_Conn_State_Connecting)	goto client_sucess;

#endif
		}

		return;
	}

	//is dtls
		if (bt > 19 && bt < 64) {
#if BPer_Enable_Dtls
			if(session->context.dtls==NULL) return;
#if BPer_Enable_Datachannel
			if (session->context.dtls->processData(session->datachannel,&session->context.dtls->session, data,size) == BPer_Ok && session->context.state == BPer_Conn_State_Connecting) {
#else
			if (session->context.dtls->processData(NULL,&session->context.dtls->session, data,size) == BPer_Ok && session->context.state == BPer_Conn_State_Connecting) {
#endif

				if(session->isControlled){
					if( session->context.dtls->session.handshake_done ) {
										session->context.state = BPer_Conn_State_Connected;
										bPer_onConnectionStateChange(session,BPer_Conn_State_Connected);
										bPer_rtcconn_startTimers(session);
					}
					return;
				}else if (session->context.dtls->session.state == BPerDtlsStateClientDone) {
					goto client_sucess;
				}
			}

#else
		if(session->isControlled){
			void* context=session->context.streamConfig->sslCallback.context;
			session->context.streamConfig->sslCallback.sslAlert(context,session->context.streamConfig->uid,"warning","CN");
		}
#endif
		}

	client_sucess:
	if(session->context.state == BPer_Conn_State_Connecting) {
		session->context.state = BPer_Conn_State_Connected;
		bPer_onConnectionStateChange(session,BPer_Conn_State_Connected);
		bPer_rtcconn_startTimers(session);
		if (session->context.streamConfig&&session->context.streamConfig->rtcCallback.sendRequest)
				session->context.streamConfig->rtcCallback.sendRequest(session->context.streamConfig->rtcCallback.context,session->context.streamConfig->uid, 0,BPer_Req_Connected);
	}
	return;
}

static void bPer_rtcconn_on_ice(BPerRtcSession *session,char* remoteIp,int32_t port) {
	if(session==NULL || remoteIp==NULL) return;
	session->context.sock->updateRemoteAddress(&session->context.sock->session,remoteIp,port);
}
static int32_t bPer_rtcconn_addAudioTrack(BPerRtcSession *session,BPerAudioCodec codec){
	if(session==NULL) return ERROR_RTC_CONNECT;
	session->context.audioCodec=codec;
	session->context.enableAudioTrack=BPerTrue;
	if(codec==BPer_AED_OPUS)
		session->audioPayloadType = BPerAudioPayloadType;

	return BPer_Ok;
}

static int32_t bPer_rtcconn_addVideoTrack(BPerRtcSession *session,BPerVideoCodec codec){
	if(session==NULL) return ERROR_RTC_CONNECT;
	session->context.videoCodec=codec;
	session->context.enableVideoTrack=BPerTrue;
	return BPer_Ok;
}

static int32_t bPer_rtcconn_addTransceiver(BPerRtcSession *session,BPerRtcDirection direction){
	if(session==NULL) return ERROR_RTC_CONNECT;
	session->context.direction=direction;
	return BPer_Ok;
}

static int32_t bPer_rtcconn_createDataChannel(BPerRtcSession* session){
	if(session==NULL) return ERROR_RTC_CONNECT;
	session->enableDatachannel=BPerTrue;
#if BPer_Enable_Datachannel
	if(session->datachannel==NULL){
		session->datachannel=(BPerDatachannel*)bPer_calloc(sizeof(BPerDatachannel),1);
		bPer_create_datachannel(session->datachannel,&session->context);
	}
#endif
	return BPer_Ok;
}


 int32_t bPer_rtcconn_getRemoteSdp(BPerRtcSession *session, char *sdpstr){
	if(session==NULL) return ERROR_RTC_SDP;
	int32_t err = BPer_Ok;

	BPerSdp sdp;
	bPer_memset(&sdp,0,sizeof(BPerSdp));
	bPer_create_rtcsdp(&sdp);
#if BPer_Enable_RTC_Audio
	if(session->remote_audio==NULL) session->remote_audio=(BPerAudioParam*)bPer_calloc(sizeof(BPerAudioParam),1);
#endif
#if BPer_Enable_RTC_Video
	if(session->remote_video==NULL) session->remote_video=(BPerVideoParam*)bPer_calloc(sizeof(BPerVideoParam),1);
#endif
	if((err=bPer_rtcsdp_parse(&sdp,sdpstr))!=BPer_Ok){
		bPer_error("sdp parse error!");
	}
	if((err=bPer_sdp_parseRemoteSdp(session,&sdp))!=BPer_Ok){
		bPer_error("parseRemoteSdp error!");
	}
	bPer_destroy_rtcsdp(&sdp);

	return err;
}




static void bPer_rtcconn_turn_receive(void *psession, char *data, int32_t size){
	if(psession==NULL || data==NULL) return;
	BPerRtcSession *session=(BPerRtcSession*)psession;
	bPer_rtcconn_receive(session,data,size);
}

static int32_t bPer_rtcconn_turn_sendData(BPerRtcSocketSession *psession, char *data, int32_t nb) {
	if (psession == NULL ||  data==NULL)	return ERROR_RTC_SOCKET;
	BPerRtcConnection* conn=(BPerRtcConnection*)psession->user;
	BPerTurnConnection* turn=conn->session->ice.session.turnconn;

	if(turn&&turn->sendData) {
		return turn->sendData(&turn->session,conn->session->ice.session.uid,data,nb);
	}
	return  ERROR_RTC_TURN;
}

static int32_t bPer_rtcconn_createOffer(BPerRtcSession *session, char **psdp){
	if(session==NULL) return ERROR_RTC_CONNECT;

	int32_t localport=session->context.streamConfig->localPort;
	BPerRtcDirection role=session->context.streamConfig->direction;
	int32_t err = BPer_Ok;
	if(!session->context.avinfo->rtc.iceUsingLocalIp&&(err=session->ice.initIce(&session->ice.session))!=BPer_Ok){
		return bPer_error_wrap(err,"ice request fail(%s)",session->ice.session.candidateType==BPerIceStun?"STUN":"TURN");
	}

	return bPer_sdp_genLocalSdp(session,localport, psdp,role);
}


static int32_t bPer_rtcconn_createAnswer(BPerRtcSession *session, char *answer){
	if(session==NULL || answer==NULL) return ERROR_RTC_CONNECT;
	session->context.streamConfig->isControlled=BPerTrue;
	session->isControlled=BPerTrue;

	int32_t localport=session->context.streamConfig->localPort;
	BPerRtcDirection role=session->context.streamConfig->direction;
	return bPer_sdp_genLocalSdp2(session,localport, answer,role);
}

static int32_t bPer_rtcconn_createHttpAnswer(BPerRtcSession *session, char *answer){
	if(session==NULL || answer==NULL) return ERROR_RTC_CONNECT;
	return bPer_sdp_getAnswerSdp(session, answer);
}


static int32_t bPer_rtcconn_setLocalDescription(BPerRtcSession* session,char* sdp){
	if(session==NULL || sdp==NULL) return ERROR_RTC_PEERCONNECTION;

	int32_t err=BPer_Ok;
	bPer_trace("\nstartRtc,port=%d",session->context.streamConfig->localPort);
	if(session->context.avinfo->sys.mediaServer==BPer_Server_P2p&&!session->context.avinfo->rtc.iceUsingLocalIp){

		if(session->ice.session.candidateType>BPerIceHost)
			bPer_trace("\nstart ice %s",session->ice.session.candidateType==BPerIceTurn?"TRUN":"STUN");


		if(session->ice.session.candidateType==BPerIceTurn&&session->ice.session.iceState==BPerIceSuccess)
			return BPer_Ok;


	}

	err=bPer_create_rtcsocket(session->context.sock,session->context.avinfo->sys.familyType,(BPerSocketProtocol)session->context.avinfo->rtc.rtcSocketProtocol,session->context.streamConfig->localPort);
	session->context.sock->session.isControlled=session->isControlled;
	if(err!=BPer_Ok) return bPer_error_wrap(err,"setLocalDescription error!");
#if BPer_Enable_Tcp_Srs
	if(session->context.avinfo->sys.mediaServer==BPer_Server_Srs)
		bPer_create_rtcsocket_srs(session->context.sock,(BPerSocketProtocol)session->context.avinfo->rtc.rtcSocketProtocol);
#endif
	bPer_rtcconn_startudp(session);
	session->context.sock->start(&session->context.sock->session);
	return err;
}

static int32_t bPer_rtcconn_setRemoteDescription(BPerRtcSession* session,char* sdpstr){
	if(session==NULL || sdpstr==NULL) return ERROR_RTC_PEERCONNECTION;
		int32_t err=BPer_Ok;

		if(session->context.avinfo->sys.mediaServer==BPer_Server_P2p && !session->context.avinfo->rtc.iceUsingLocalIp
					&& session->ice.session.iceState==BPerIceFail) {
			bPer_error("p2p ice error!");
			return ERROR_RTC_PEERCONNECTION;
		}
		session->isControlled=session->context.streamConfig->isControlled;

		BPerSdp sdp;
		bPer_memset(&sdp,0,sizeof(BPerSdp));
		bPer_create_rtcsdp(&sdp);
	#if BPer_Enable_RTC_Audio
		if(session->remote_audio==NULL) session->remote_audio=(BPerAudioParam*)bPer_calloc(sizeof(BPerAudioParam),1);
	#endif
	#if BPer_Enable_RTC_Video
		if(session->remote_video==NULL) session->remote_video=(BPerVideoParam*)bPer_calloc(sizeof(BPerVideoParam),1);
	#endif
		if((err=bPer_rtcsdp_parse(&sdp,sdpstr))!=BPer_Ok){
			bPer_error("sdp parse error!");
		}
		if(err==BPer_Ok&&(err=bPer_sdp_parseRemoteSdp(session,&sdp))!=BPer_Ok){
			bPer_error("parseRemoteSdp error!");
		}
		bPer_destroy_rtcsdp(&sdp);

		session->ice.session.stun.createRequestStunPacket(session,session->remoteIcePwd);
		if(session->context.avinfo->sys.mediaServer==BPer_Server_P2p&&!session->context.avinfo->rtc.iceUsingLocalIp){
			if((err=session->ice.iceHandle(&session->ice.session,session,bPer_rtcconn_turn_receive,
						session->context.streamConfig->remoteIp,session->context.streamConfig->remotePort))!=BPer_Ok){
					return bPer_error_wrap(err,"setLocalDescription error!");
				}


			if(session->ice.session.candidateType==BPerIceTurn&&session->ice.session.iceState==BPerIceSuccess){
				err=bPer_create_rtcsocket(session->context.sock,session->context.avinfo->sys.familyType,BPer_Socket_Protocol_Udp,session->context.streamConfig->localPort);
				if(err!=BPer_Ok) return bPer_error_wrap(err,"setLocalDescription error!");
				session->context.sock->write=bPer_rtcconn_turn_sendData;
				bPer_rtcconn_startudp(session);
				g_bPer_startStunTimer(session->context.sock->session.user);
				return BPer_Ok;
			}
		}

        if(!session->isControlled)
            session->context.sock->updateRemoteAddress(&session->context.sock->session,session->context.streamConfig->remoteIp,session->isControlled?0:session->context.streamConfig->remotePort);


		return err;
}


static BPerBool bPer_rtcconn_isConnected(BPerRtcSession* session){
	if (session == NULL)		return BPerFalse;
	return session->context.state == BPer_Conn_State_Connected;
}

int32_t bPer_create_rtcConnection(BPerRtcConnection* conn,BPerStreamConfig* streamconfig,BPerAVInfo* avinfo){
	if (conn == NULL ||streamconfig == NULL || avinfo==NULL)		return ERROR_RTC_CONNECT;
    BPerRtcSession* session=(BPerRtcSession*)bPer_calloc(sizeof(BPerRtcSession),1);
	conn->session=session;
	bPer_create_rtcContext(&session->context);
	bPer_create_ice(&session->ice,streamconfig,avinfo);
	bPer_memset(&session->rtcp_compound,0,sizeof(BPerRtcpCompound));
	bPer_create_rtcpCompound(&session->rtcp_compound);

	session->context.streamConfig = streamconfig;
	session->context.sock->session.user = conn;
	session->context.sock->session.receive = g_session_receive;
	session->context.sock->session.startStunTimer = g_bPer_startStunTimer;

	session->context.avinfo=avinfo;

	if(streamconfig->remotePort==0)
		streamconfig->remotePort=8000;

	session->isControlled=BPerFalse;
	session->enableDatachannel=BPerFalse;
	session->h264PayloadType=BPerH264PayloadType;
	session->h265PayloadType=BPerH265PayloadType;
	session->audioPayloadType = BPerAudioPayloadType;


	conn->close=bPer_rtcconn_close;
	conn->on_video=bPer_rtcconn_onVideo;
	conn->on_audio=bPer_rtcconn_onAudio;
	conn->on_message=bPer_rtcconn_onMessage;
	conn->notify=bPer_rtcconn_notify;
	conn->isAlive=bPer_rtcconn_isAlive;

	conn->receive=bPer_rtcconn_receive;
	conn->updateCandidateAddress=bPer_rtcconn_on_ice;
	conn->onConnectionStateChange=bPer_onConnectionStateChange;
	conn->setLocalDescription=bPer_rtcconn_setLocalDescription;
	conn->setRemoteDescription=bPer_rtcconn_setRemoteDescription;
	conn->createOffer=bPer_rtcconn_createOffer;
	conn->createAnswer=bPer_rtcconn_createAnswer;
	conn->createHttpAnswer=bPer_rtcconn_createHttpAnswer;
	conn->createDataChannel=bPer_rtcconn_createDataChannel;
	conn->isConnected=bPer_rtcconn_isConnected;

	conn->addAudioTrack=bPer_rtcconn_addAudioTrack;
	conn->addVideoTrack=bPer_rtcconn_addVideoTrack;
	conn->addTransceiver=bPer_rtcconn_addTransceiver;
	return BPer_Ok;

}

void bPer_destroy_rtcConnection(BPerRtcConnection *conn) {
	if (conn == NULL)		return;
	BPerRtcSession* session=(BPerRtcSession*)conn->session;
	session->activeState = BPerFalse;

	//bPer_stop(session->20ms);
	bPer_timer_stop(session->tm_1s);
	bPer_timer_stop(session->tm_100ms);
	bPer_destroy_timer(session->tm_1s);
	bPer_destroy_timer(session->tm_100ms);
	// bPer_free(session->20ms);
	bPer_free(session->tm_1s);
	bPer_free(session->tm_100ms);

#if BPer_Enable_RTC_Audio
	bPer_destroy_pushAudio(session->pushAudio);
	bPer_free(session->remote_audio);
#endif

#if BPer_Enable_RTC_Video
	bPer_destroy_pushH264(session->pushH264);
	bPer_free(session->pushH264);
	#if	BPer_Enable_H265_Encoding
	bPer_destroy_pushH265(session->pushH265);
	bPer_free(session->pushH265);
	#endif
	bPer_free(session->remote_video);
#endif

	bPer_destroy_rtcpush(session->push);
	bPer_free(session->push);

	bPer_destroy_rtcplay(session->play);
	bPer_free(session->play);

#if BPer_Enable_Datachannel
	bPer_destroy_datachannel(session->datachannel);
	bPer_free(session->datachannel);
#endif

	bPer_destroy_ice(&session->ice);
	bPer_destroy_rtcContext(&session->context);

	if(session->playRtpBuffer){
		bPer_destroy_rtpBuffer(session->playRtpBuffer);
		bPer_free(session->playRtpBuffer);
	}

	bPer_destroy_rtcpCompound(&session->rtcp_compound);

#if BPer_Enable_RTC_Audio
	if(session->pushAudioRtpBuffer){
		bPer_destroy_rtpBuffer(session->pushAudioRtpBuffer);
		bPer_free(session->pushAudioRtpBuffer);
	}
#endif

#if BPer_Enable_RTC_Video
	if(session->pushVideoRtpBuffer){
		bPer_destroy_rtpBuffer(session->pushVideoRtpBuffer);
		bPer_free(session->pushVideoRtpBuffer);
	}
#endif
	bPer_free(conn->session);
}

